/*
 * @Author: wuqingchun
 * @Date: 2024-07-23 13:49:25
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-07-26 09:02:13
 */
#ifndef __COMMON_H__
#define __COMMON_H__

#include <sys/mman.h>

#include <cstddef>
#include <cassert>

#ifndef PAGE_SIZE
#define PAGE_SIZE     4096ul
#define PAGE_SHIFT    12
#define PAGE_MASK     (PAGE_SIZE - 1)
#endif


inline static void* OsAlloc(size_t ulPageNum)
{
    void* pMem = mmap(nullptr, ulPageNum << PAGE_SHIFT, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (pMem == MAP_FAILED)
    {
        return nullptr;
    }

    return pMem;
}

inline static void OsFree(void* pMem, size_t ulPageNum)
{
    munmap(pMem, ulPageNum << PAGE_SHIFT);
}


/**
 * @brief 空闲链表
 * 
 */
class FreeList
{
public:
    static void* GetNextObject(void* pObj)
    {
        return *(void**)pObj;
    }

    static void ClearNextObject(void* pObj)
    {
        *(void**)pObj = nullptr;
    }
public:
    bool Empty()
    {
        return m_pFreeList == nullptr;
    }

    void Push(void* pObj)
    {
        *(void**)pObj = m_pFreeList;
        m_pFreeList = pObj;
        m_ulObjNum++;
    }

    void* Pop()
    {
        void* pObj = m_pFreeList;
        m_pFreeList = GetNextObject(pObj);
        m_ulObjNum--;
    }

    void BatchPush(void* pHead, void* pTail, size_t ulNum)
    {
        *(void**)pTail = m_pFreeList;
        m_pFreeList = pHead;

        m_ulObjNum += ulNum;
    }

    size_t GetExpandNum()
    {
        return m_ulExpandNum;
    }

    void IncrExpandNum()
    {
        m_ulExpandNum += 1;
    }
    
private:
    void* m_pFreeList = nullptr;
    size_t m_ulObjNum = 0;
    size_t m_ulExpandNum = 1; // 当空闲链表为空时，需要增加的节点个数
};

// 内存大小对齐策略类
class SizeAlignStrategy
{
public:
    static constexpr size_t PAGE_NUM_MAX = 128; // PageCache 能够管理的连续最大内存页的数量
    static const size_t MAX_BYTES = 256 * 1024; // Thread Cache 支持不超过256kb大小的内存申请请求
    static constexpr size_t NR_FREELIST = 208;
public:
    /**
     * @brief 根据申请的内存大小计算对齐之后的大小
     * 
     * @param ulSize  - 申请的内存大小
     * @return size_t - 对齐后的大小
     */
    static size_t AlignSize(size_t ulSize)
    {
        assert(ulSize <= MAX_BYTES);

        if (ulSize <= 128)
        {
            // [1, 128] 大小范围内的内存使用8字节对齐
            return __AlignTo2N(ulSize, 8);
        }

        if (ulSize <= 1024)
        {
            // [129, 1024] 大小范围内的内存使用16字节对齐
            return __AlignTo2N(ulSize, 16);
        }

        if (ulSize <= 8 * 1024)
        {
            // [1025, 8*1024] 大小范围内的内存使用128字节对齐
            return __AlignTo2N(ulSize, 128);
        }

        if (ulSize <= 64 * 1024)
        {
            // [8*1024 + 1, 64KB] 大小范围内的内存使用1024字节对齐
            return __AlignTo2N(ulSize, 1024);
        }

        if (ulSize <= 256 * 1024)
        {
            // [64KB + 1, 256KB] 大小范围内的内存使用8K字节对齐
            return __AlignTo2N(ulSize, 8*1024);
        }

        return 0; // 非法值
    }

    static size_t Index(size_t ulSize)
    {
        if (ulSize <= 128)
        {
            // [1, 128] 大小范围内的内存使用8字节对齐
            return ulSize >> 3;
        }

        if (ulSize <= 1024)
        {
            // [129, 1024] 大小范围内的内存使用16字节对齐
            return ulSize >> 4;
        }

        if (ulSize <= 8 * 1024)
        {
            // [1025, 8*1024] 大小范围内的内存使用128字节对齐
            return ulSize >> 7;
        }

        if (ulSize <= 64 * 1024)
        {
            // [8*1024 + 1, 64KB] 大小范围内的内存使用1024字节对齐
            return ulSize >> 10;
        }

        if (ulSize <= 256 * 1024)
        {
            // [64KB + 1, 256KB] 大小范围内的内存使用8K字节对齐
            return ulSize >> 13;
        }
    }
    
    /**
     * @brief 
        从Central Cache获取对象时，也是有对应的策略的。Central Cache 是多线程共享的，频繁的申请要加锁，会有锁的竞争，会降低效率的。
        所以，参考tcmalloc，使用一定的反馈算法来一次批量的申请内存。

        比如频繁的从Central Cache 中申请8Kb大小的内存时，会根据慢反馈调节算法，一次批量的申请多个大小的内存块。

        比如第一次申请8Kb，申请一个，第二次申请时，申请两个，返回一个，多申请的交给自由链表管理，下次再申请8kb的时候，
        Thread Cache的自由链表就有内存了，不用再去向Central Cache申请了。以此类推，当然，这个算法还要定一个上限，
        不能一下申请好几万个交给自由链表管理。上限规定，一次最多申请512个内存块，申请太多会成负担的。
        【这个机制和网络中的tcp协议的慢启动 机制类似】
     * @param size 
     * @return size_t 
     */
    static size_t NumMoveSize(size_t size)
    {
        assert(size > 0);

        // [2, 512]，一次批量移动多少个对象的(慢启动)上限值
        // 小对象一次批量上限高
        // 小对象一次批量上限低
        int num = MAX_BYTES / size;
        if (num < 2)
        {
            num = 2;
        }

        if (num > 512)
        {
            num = 512;
        }

        return num;
    }

    static size_t CalculatePageNum(size_t ulObjSize)
    {
        size_t ulNum = ulObjSize >> PAGE_SHIFT;
        return ulNum + 1;
    }
private:
    /**
     * @brief 大小对齐到2的整数次幂
     * 
     * @param ulSize  - 需要执行对齐的大小
     * @param ul2Pow  - 对齐数，2的整数次幂
     * @return size_t - 对齐后的大小
     */
    static size_t __AlignTo2N(size_t ulSize, size_t ulAgin)
    {
        return (ulSize + ulAgin - 1) & (~(ulAgin - 1));
        //return ((ulSize + ulAlign - 1) / ulAlign) * ulAlign;
    }
};

#endif //__COMMON_H__